package proxy

// 将反向代理提升到Server的高度

import (
	"bytes"
	"compress/gzip"
	"io/ioutil"
	"math/rand"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strconv"
	"strings"
	"time"
)

// var addr = "127.0.0.1:2002"

// func main() {
// 	//rs1 := "http://www.baidu.com"
// 	rs1 := "http://127.0.0.1:2003"
// 	url1, err1 := url.Parse(rs1)
// 	if err1 != nil {
// 		log.Println(err1)
// 	}

// 	//rs2 := "http://www.baidu.com"
// 	rs2 := "http://127.0.0.1:2004"
// 	url2, err2 := url.Parse(rs2)
// 	if err2 != nil {
// 		log.Println(err2)
// 	}
// 	urls := []*url.URL{url1, url2}
// 	proxy := NewMultipleHostsReverseProxy(urls)
// 	log.Println("Starting httpserver at " + addr)
// 	log.Fatal(http.ListenAndServe(addr, proxy))
// }
type HttpReverseProxy struct {
	Name string
	httputil.ReverseProxy
	Position int
}

// 连接池，是ReverseProxy对象的属性
var defaultTransport = &http.Transport{
	DialContext: (&net.Dialer{
		Timeout:   30 * time.Second, //连接超时
		KeepAlive: 30 * time.Second, //长连接超时时间
	}).DialContext,
	MaxIdleConns:          100,              //最大空闲连接
	IdleConnTimeout:       90 * time.Second, //空闲超时时间
	TLSHandshakeTimeout:   10 * time.Second, //tls握手超时时间
	ExpectContinueTimeout: 1 * time.Second,  //100-continue 超时时间
}

var defaultDirecrot = func(req *http.Request) {
	//url_rewrite
	//127.0.0.1:2002/dir/abc ==> 127.0.0.1:2003/base/abc ??
	//127.0.0.1:2002/dir/abc ==> 127.0.0.1:2002/abc
	//127.0.0.1:2002/abc ==> 127.0.0.1:2003/base/abc
	// re, _ := regexp.Compile("^/dir(.*)")
	// req.URL.Path = re.ReplaceAllString(req.URL.Path, "$1")

	//随机负载均衡
	// targetIndex := rand.Intn(len(targets))
	// target := targets[targetIndex]
	// targetQuery := target.RawQuery
	// req.URL.Scheme = target.Scheme
	// req.URL.Host = target.Host

	// req.Host = target.Host

	// // 可以增加 url地址重写功能
	// // req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)

	// if targetQuery == "" || req.URL.RawQuery == "" {
	// 	req.URL.RawQuery = targetQuery + req.URL.RawQuery
	// } else {
	// 	req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
	// }
	// if _, ok := req.Header["User-Agent"]; !ok {
	// 	req.Header.Set("User-Agent", "user-agent")
	// }
	//只在第一代理中设置此header头
	//req.Header.Set("X-Real-Ip", req.RemoteAddr)
}

func NewMultipleHostsReverseProxy(targets []*url.URL) *httputil.ReverseProxy {
	//请求协调者
	director := func(req *http.Request) {
		//url_rewrite
		//127.0.0.1:2002/dir/abc ==> 127.0.0.1:2003/base/abc ??
		//127.0.0.1:2002/dir/abc ==> 127.0.0.1:2002/abc
		//127.0.0.1:2002/abc ==> 127.0.0.1:2003/base/abc
		// re, _ := regexp.Compile("^/dir(.*)")
		// req.URL.Path = re.ReplaceAllString(req.URL.Path, "$1")

		//随机负载均衡
		targetIndex := rand.Intn(len(targets))
		target := targets[targetIndex]
		targetQuery := target.RawQuery
		req.URL.Scheme = target.Scheme
		req.URL.Host = target.Host

		req.Host = target.Host

		// 可以增加 url地址重写功能
		// req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)

		if targetQuery == "" || req.URL.RawQuery == "" {
			req.URL.RawQuery = targetQuery + req.URL.RawQuery
		} else {
			req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
		}
		if _, ok := req.Header["User-Agent"]; !ok {
			req.Header.Set("User-Agent", "user-agent")
		}
		//只在第一代理中设置此header头
		//req.Header.Set("X-Real-Ip", req.RemoteAddr)
	}

	//更改内容
	modifyFunc := func(resp *http.Response) error {

		// 需要兼容websocket
		if strings.Contains(resp.Header.Get("Connection"), "Upgrade") {
			return nil
		}
		var payload []byte
		var readErr error

		// 兼容gzip压缩
		if strings.Contains(resp.Header.Get("Content-Encoding"), "gzip") {
			gr, err := gzip.NewReader(resp.Body)
			if err != nil {
				return err
			}
			payload, readErr = ioutil.ReadAll(gr)
			resp.Header.Del("Content-Encoding")
		} else {
			payload, readErr = ioutil.ReadAll(resp.Body)
		}
		if readErr != nil {
			return readErr
		}

		// 异常请求时设置StatusCode
		if resp.StatusCode != 200 {
			payload = []byte("StatusCode error:" + string(payload))
		}

		// 因为预读了数据所以内容重新回写
		resp.Body = ioutil.NopCloser(bytes.NewBuffer(payload))
		resp.ContentLength = int64(len(payload))
		resp.Header.Set("Content-Length", strconv.FormatInt(int64(len(payload)), 10))
		return nil
	}

	//错误回调 ：关闭real_server时测试，错误回调
	errFunc := func(w http.ResponseWriter, r *http.Request, err error) {
		http.Error(w, "ErrorHandler error:"+err.Error(), 500)
	}

	return &httputil.ReverseProxy{
		Director: director,
		// Transport:      transport,
		ModifyResponse: modifyFunc,
		ErrorHandler:   errFunc}
}

func singleJoiningSlash(a, b string) string {
	aslash := strings.HasSuffix(a, "/")
	bslash := strings.HasPrefix(b, "/")
	switch {
	case aslash && bslash:
		return a + b[1:]
	case !aslash && !bslash:
		return a + "/" + b
	}
	return a + b
}
